#include "omci_adpt_common.h"
#include "omci_adpt_general.h"

#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/list.h>
#include <pthread.h>

#include <libubus.h>
#include <json-c/json.h>
#include <string.h>
#include <stdint.h>

#include "omci_adpt_voip.h"

typedef int (*task_proc_func)();

#define UBUS_VOIP_OBJECT_NAME "com.network.voip"
#define UBUS_METHOD_SET_BASIC_COMM_CONF "set_basic_comm_config"

#define UBUS_METHOD_SET_BASIC_USER_CONF "set_basic_user_config"

#define MAX_PRINT_STR_LEN			168

static basic_comm_params g_voip_basic_comm;
static basic_user_params g_voip_basic_user;

void init_voip_basic_comm()
{
    memset(&g_voip_basic_comm, 0, sizeof(g_voip_basic_comm));

    strncpy(g_voip_basic_comm.protocol, "SIP", sizeof(g_voip_basic_comm.protocol));
    strncpy(g_voip_basic_comm.region, "CHN-CHINA", sizeof(g_voip_basic_comm.region));
    strncpy(g_voip_basic_comm.register_server, "0.0.0.0", sizeof(g_voip_basic_comm.register_server));
    g_voip_basic_comm.register_server_port = 5060;

    strncpy(g_voip_basic_comm.proxy_server, "0.0.0.0", sizeof(g_voip_basic_comm.register_server));
    g_voip_basic_comm.proxy_server_port = 5060;
    
    strncpy(g_voip_basic_comm.outbound_proxy, "0.0.0.0", sizeof(g_voip_basic_comm.register_server));
    g_voip_basic_comm.outbound_proxy_port = 5060;

    strncpy(g_voip_basic_comm.backup_register_server, "0.0.0.0", sizeof(g_voip_basic_comm.register_server));
    g_voip_basic_comm.backup_register_server_port = 5060;

    strncpy(g_voip_basic_comm.backup_proxy_server, "0.0.0.0", sizeof(g_voip_basic_comm.register_server));
    g_voip_basic_comm.backup_proxy_server_port = 5060;

    strncpy(g_voip_basic_comm.backup_outbound_proxy, "0.0.0.0", sizeof(g_voip_basic_comm.register_server));
    g_voip_basic_comm.backup_outbound_proxy_port = 5060;

    g_voip_basic_comm.reg_expire_timer = 1800;
    g_voip_basic_comm.session_expires = 1800;
    g_voip_basic_comm.min_session_expires = 1800;
    g_voip_basic_comm.options_interval_time_enable = 1;
    g_voip_basic_comm.options_interval_time = 60;
}

json_object *create_voip_basic_comm_json() {
    json_object *voipBasicComm = json_object_new_object();

    json_object_object_add(voipBasicComm, "protocol", 	        json_object_new_string(g_voip_basic_comm.protocol));
    json_object_object_add(voipBasicComm, "region",             json_object_new_string(g_voip_basic_comm.region));
    json_object_object_add(voipBasicComm, "register_server", 	json_object_new_string(g_voip_basic_comm.register_server));
    json_object_object_add(voipBasicComm, "register_server_port", 		json_object_new_int(g_voip_basic_comm.register_server_port));
    json_object_object_add(voipBasicComm, "proxy_server", 		json_object_new_string(g_voip_basic_comm.proxy_server));
    json_object_object_add(voipBasicComm, "proxy_server_port",  json_object_new_int(g_voip_basic_comm.proxy_server_port));
    json_object_object_add(voipBasicComm, "outbound_proxy",     json_object_new_string(g_voip_basic_comm.outbound_proxy));
    json_object_object_add(voipBasicComm, "outbound_proxy_port", json_object_new_int(g_voip_basic_comm.outbound_proxy_port));

    json_object_object_add(voipBasicComm, "backup_register_server", 		json_object_new_string(g_voip_basic_comm.backup_register_server));
    json_object_object_add(voipBasicComm, "backup_register_server_port", 	json_object_new_int(g_voip_basic_comm.backup_register_server_port));
    json_object_object_add(voipBasicComm, "backup_proxy_server", 	        json_object_new_string(g_voip_basic_comm.backup_proxy_server));
    json_object_object_add(voipBasicComm, "backup_proxy_server_port",   json_object_new_int(g_voip_basic_comm.backup_proxy_server_port));
    json_object_object_add(voipBasicComm, "backup_outbound_proxy", 		json_object_new_string(g_voip_basic_comm.backup_outbound_proxy));
    json_object_object_add(voipBasicComm, "backup_outbound_proxy_port", json_object_new_int(g_voip_basic_comm.backup_outbound_proxy_port));
    json_object_object_add(voipBasicComm, "reg_expire_timer", 	        json_object_new_int(g_voip_basic_comm.reg_expire_timer));
    json_object_object_add(voipBasicComm, "session_expires", 	        json_object_new_int(g_voip_basic_comm.session_expires));
    json_object_object_add(voipBasicComm, "min_session_expires", 	        json_object_new_int(g_voip_basic_comm.min_session_expires));
    json_object_object_add(voipBasicComm, "options_interval_time_enable", 	json_object_new_int(g_voip_basic_comm.options_interval_time_enable));
    json_object_object_add(voipBasicComm, "options_interval_time", 	        json_object_new_int(g_voip_basic_comm.options_interval_time));

    return voipBasicComm;
}

void init_voip_basic_user()
{
    memset(&g_voip_basic_user, 0, sizeof(g_voip_basic_user));

    g_voip_basic_user.port_index = 0;
    g_voip_basic_user.enable = 0;
}

json_object *create_voip_basic_user_json() {
    json_object *voipBasicUser = json_object_new_object();

    json_object_object_add(voipBasicUser, "port_index", 		json_object_new_int(g_voip_basic_user.port_index));
    json_object_object_add(voipBasicUser, "enable", 	        json_object_new_int(g_voip_basic_user.enable));
    json_object_object_add(voipBasicUser, "telephone_number", 	json_object_new_string(g_voip_basic_user.telephone_number));
    json_object_object_add(voipBasicUser, "account",            json_object_new_string(g_voip_basic_user.account));
    json_object_object_add(voipBasicUser, "password", 		    json_object_new_string(g_voip_basic_user.password));

    return voipBasicUser;
}

void init_voip_config()
{
    init_voip_basic_comm();
    init_voip_basic_user();
}

int voip_set_proxy_server(char *proxy_server)
{
    strncpy(g_voip_basic_comm.proxy_server, proxy_server, sizeof(g_voip_basic_comm.proxy_server));

    return 0;
}

int voip_set_register_server(char *register_server)
{
    strncpy(g_voip_basic_comm.register_server, register_server, sizeof(g_voip_basic_comm.register_server));

    return 0;
}

int voip_set_telephone_number(char *telephone_number)
{
    strncpy(g_voip_basic_user.telephone_number, telephone_number, sizeof(g_voip_basic_user.telephone_number));

    return 0;
}

int voip_set_user_name(char *user_name)
{
    strncpy(g_voip_basic_user.account, user_name, sizeof(g_voip_basic_user.account));

    return 0;
}

int voip_set_user_password(char *password)
{
    strncpy(g_voip_basic_user.password, password, sizeof(g_voip_basic_user.password));

    return 0;
}

int voip_set_port_id(uint8_t port_id)
{
    g_voip_basic_user.port_index = port_id - 1;  // 偏移1位

    return 0;
}

static struct ubus_context *g_ubus_ctx = NULL;
static struct blob_buf g_ubus_buf;

static int ubus_call_method(struct ubus_context *ubus_ctx,
								char *ubus_name,
								char *ubus_method,
								struct blob_buf b_buf,
								ubus_data_handler_t cb, void*data)
{
	unsigned int id = 0;
	int ret;

	ret =  ubus_lookup_id(ubus_ctx, ubus_name, &id);
	if (0 != ret)
	{
		omcidbgPrintf(OMCI_DEBUG_LEVEL_ERROR, "[%s][%d]: Error. Can't find %s\n",__FUNCTION__,__LINE__, ubus_name);
        return -1;
	}

	ret = ubus_invoke(ubus_ctx, id, ubus_method, b_buf.head, cb, (void *)data, 0);
	if (ret != 0) {
		omcidbgPrintf(OMCI_DEBUG_LEVEL_ERROR, "[%s][%d]: ubus_invoke error \n",__FUNCTION__,__LINE__);
	}

    return 0;
}

static void cb_reply_status(struct ubus_request *req, int type, struct blob_attr *msg)
{
	char **json_s = (char **)req->priv;
	char *str = blobmsg_format_json(msg, true);

	size_t len = strlen(str);
	*json_s = malloc(len + 1);
	if (*json_s == NULL)
	{
		free(str); 
		return;
	}

	strcpy(*json_s, str);

	if (str != NULL)
		free(str);

	return;
}

int ubus_call(char *objects, char *method, const char* message, char** cjson_s)
{
	omcidbgPrintf(OMCI_DEBUG_LEVEL_TRACE, "[%s][%d]: ubus_call %s %s %s\n",__FUNCTION__,__LINE__, objects, method, message);

	if (message != NULL && strlen(message) > MAX_PRINT_STR_LEN) {
		omcidbgPrintf(OMCI_DEBUG_LEVEL_TRACE, "%s\n", (message + MAX_PRINT_STR_LEN));
		if (strlen(message) > 2 * MAX_PRINT_STR_LEN) {
			omcidbgPrintf(OMCI_DEBUG_LEVEL_TRACE, "%s\n", (message + 2 * MAX_PRINT_STR_LEN));
		}
	}
	
    if(NULL == objects || NULL == method) {
        return -1;
    }

    struct json_object *json_obj = NULL;
    blob_buf_init(&g_ubus_buf, 0);
    if(message == NULL)
    {     
        void *obj = blobmsg_open_table(&g_ubus_buf, NULL);
        blobmsg_close_table(&g_ubus_buf, obj);
    }
    else
    {
        json_obj = json_tokener_parse(message);
        if (json_obj == NULL || json_object_get_type(json_obj) != json_type_object) {
			omcidbgPrintf(OMCI_DEBUG_LEVEL_ERROR, "[%s][%d]: Command failed: Invalid argument\n",__FUNCTION__,__LINE__);
            return -1;
        }

        if (!blobmsg_add_object(&g_ubus_buf, json_obj)) {
			omcidbgPrintf(OMCI_DEBUG_LEVEL_ERROR, "[%s][%d]: Command failed: Invalid argument\n",__FUNCTION__,__LINE__);
            return -1;
	    }
    }

    ubus_call_method(g_ubus_ctx, objects, method, g_ubus_buf, cb_reply_status, (void *)cjson_s);

	json_object_put(json_obj);
	blob_buf_free(&g_ubus_buf);
    return 0;
}

int ubus_client_init(void)
{
	g_ubus_ctx = ubus_connect(NULL);
	if (NULL == g_ubus_ctx)
	{
		omcidbgPrintf(OMCI_DEBUG_LEVEL_ERROR, "[%s][%d]: ubus connect failed\n",__FUNCTION__,__LINE__);
		return -1;
	}

	ubus_add_uloop(g_ubus_ctx);
	return 0;
}

void ubus_client_exit(void)
{
	if (g_ubus_ctx) {
        ubus_free(g_ubus_ctx);
		omcidbgPrintf(OMCI_DEBUG_LEVEL_ERROR, "[%s][%d]: ubus client exit\n",__FUNCTION__,__LINE__);
    }
}

int ubus_set_basic_comm_params()
{
    char *s = NULL;
    int ret = 0;

	json_object *json_obj = create_voip_basic_comm_json();
    const char *json_str = json_object_to_json_string(json_obj);

    ubus_call(UBUS_VOIP_OBJECT_NAME, UBUS_METHOD_SET_BASIC_COMM_CONF, json_str, &s);
    if(s == NULL) {
        return -1;
    }

	omcidbgPrintf(OMCI_DEBUG_LEVEL_TRACE, "[%s][%d]: ubus_call %s\n",__FUNCTION__,__LINE__, s);

    json_object_put(json_obj);
    if(s != NULL) {
        free(s);
    }

    return ret;
}

int ubus_set_basic_user_params()
{
    char *s = NULL;
    int ret = 0;

	json_object *json_obj = create_voip_basic_user_json();
    const char *json_str = json_object_to_json_string(json_obj);

    ubus_call(UBUS_VOIP_OBJECT_NAME, UBUS_METHOD_SET_BASIC_USER_CONF, json_str, &s);
    if(s == NULL) {
        return -1;
    }

	omcidbgPrintf(OMCI_DEBUG_LEVEL_TRACE, "[%s][%d]: ubus_call %s\n",__FUNCTION__,__LINE__, s);

    json_object_put(json_obj);
    if(s != NULL) {
        free(s);
    }

    return ret;
}

int careate_thread_voip_proc(task_proc_func func)
{
    pthread_t thread_id;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

	int ret = pthread_create(&thread_id, &attr, (void *)func, NULL);
	if (ret != 0) {
		omcidbgPrintf(OMCI_DEBUG_LEVEL_TRACE, "[%s][%d]: Failed to create thread ret %d\n",__FUNCTION__,__LINE__, ret);
		return -1;
	}

    pthread_attr_destroy(&attr);
	return ret;
}

int omci_set_voip_basic_comm()
{
    int ret = ubus_set_basic_comm_params();

    omcidbgPrintf(OMCI_DEBUG_LEVEL_TRACE, "[%s][%d]: ret %d\n",__FUNCTION__,__LINE__, ret);

	return ret;
}

int omci_set_voip_basic_user()
{
    int ret = ubus_set_basic_user_params();

    omcidbgPrintf(OMCI_DEBUG_LEVEL_TRACE, "[%s][%d]: ret %d\n",__FUNCTION__,__LINE__, ret);

	return ret;
}

